#include <map>
#include <string>
#include <iostream>
#include <sstream>
#include "Move.h"
#include "DebugTest.h"
#include "Engine.h"
using namespace std;
/*using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::map;
using std::istringstream;*/

#define STR_RESULT_OK "-- OK --"
#define STR_RESULT_KO "** KO **"

void DebugTest::LaunchTests()
{
	map<string,string> legalMovesDatabase;// = new Hashtable();
	map<string,string> bestMovesDatabase;// = new Hashtable();
	map<string,string> matesDatabase;// = new Hashtable();

	initLegalMovesDatabase(legalMovesDatabase);
	initBestMovesDatabase(bestMovesDatabase);
	initMatesDatabase(matesDatabase);

	map<string,string> noCheck;// = new Hashtable();
	map<string,string> inCheck;// = new Hashtable();
	map<string,string> inCheckmate;// = new Hashtable();

	initNoCheck(noCheck);
	initInCheck(inCheck);
	initInCheckmate(inCheckmate);

	cout << "*****************************************\n"
		"** FUNCTIONAL TEST SET                 **\n"
		"*****************************************\n"
		" 0 - TestCheck                           \n"
		" 1 - TestLegalMoves                      \n"
		" 2 - TestPlayUnplayMoves                 \n"
		" 3 - TestAlphaBeta                       \n"
		" 4 - TestExpectedBestMove                \n"
		" 5 - TestAlphabetaVsNegmax               \n"
		" 6 - TestAlphabetaVSNegascout            \n"
		" 7 - TestAlphaBetaVSIterative            \n"
		" 8 - TestSelectiveDepth                  \n"
		" 9 - TestMates                           \n"
		"10 - TestAlphaBetaIter                   \n"
		"11 - TestBitboards                       \n"
		"*****************************************\n";
	int choice, duration;

	cout << "Test:";
	cin >> choice;

	switch (choice)
	{
	case 0:
		TestIsCheck(noCheck,inCheck,inCheckmate);
		break;
	case 1:
		TestLegalMoves(legalMovesDatabase);
		break;
	case 2:



		TestPlayUnplayMoves(legalMovesDatabase, true);
		break;
	case 3:
		TestAlphaBeta();
		break;
	case 4:
		cout << "Duration (ms):";
		cin >> duration;
		TestExpectedBestMove(bestMovesDatabase, duration);
		break;
	case 5:
		cout << "Duration (ms):";
		cin >> duration;
		TestABMinmaxVsNegmax(bestMovesDatabase, duration);
		break;
	case 6:
		cout << "Duration (ms):";
		cin >> duration;
		TestABMinmaxVSNegascout(bestMovesDatabase, duration);
		break;
	case 7:
		cout << "Duration (ms):";
		cin >> duration;
		TestAlphaBetaRecVSIter(bestMovesDatabase, duration);
		break;
	case 8:
		cout << "Duration (ms):";
		cin >> duration;
		TestSelectiveDepth(bestMovesDatabase, duration);
		break;
	case 9:
		cout << "Duration (ms):";
		cin >> duration;
		TestExpectedBestMove(matesDatabase, duration);
		break;
	case 10:
		TestAlphaBetaIter();
		break;
	case 11:
		TestBitboards();
		break;
	default:
		break;
	}

}

void DebugTest::TestBitboards()
{
	// No need to implement this test

}

void DebugTest::TestAlphaBetaIter()
{
	cout << "*** Calling iterative version ***" << endl;
	ChessEngine.SetPosition("k7/8/K7/8/8/6Rr/8/8 w - - 10 10"); //expected g3g8 mate 1
	ChessEngine.searchImplementation = SEARCH_IMPL_ITERATIVE_AB;
	ChessEngine.SetDepthMode(3);
	ChessEngine.Start();

	//int score2 = engine2.GetBestScore();
	//Move? bm2 = engine2.GetBestMove();

}


void DebugTest::initMatesDatabase(map<string,string> & matesDatabase)
{
	matesDatabase["k7/8/K7/8/8/6Rr/8/8 w - - 10 10"] = "dilema gain or mate (white to move):g3g8"; //dilema gain or mate
	matesDatabase["K7/8/k7/8/8/6rR/8/8 b - - 10 10"] =  "dilema gain or mate (black to move):g3g8"; //dilema gain or mate
	matesDatabase["1k6/8/1K6/8/7R/8/8/8 w - - 10 10"] =  "obvious mate1 (white to play):h4h8"; //obvious mate1
	matesDatabase["1K6/8/1k6/8/7r/8/8/8 b - - 10 10"] =  "obvious mate1 (black to play):h4h8"; //obvious mate1
	matesDatabase["6k1/4pp2/3p2p1/2p4p/1pPqPPPP/1P1P4/3Q2K1/5r2 b - - 0 36"] = "back mates in 2 (Queen's rush):d4g1"; //mate in 2
}

void DebugTest::initBestMovesDatabase(map<string,string> & bestMovesDatabase)
{

	// version 0.2.3 new test positions (winning combinations)
	//bestMovesDatabase["5nnr/P1N2k2/8/1P4Bp/7P/3K1P2/6p1/b6R w - - 0 34"] = "tactical move 1 >> lateral move (rook takes bishop):h1a1"; //rook takes bishop
	//bestMovesDatabase["6nr/P1n2k2/8/1P2K1Bp/7P/5P2/6p1/6R1 w - - 0 37"] = "tactical move 2 >> Support pawn to ensure promotion:b5b6";

	// version 0.2.1 basic test positions
	
	bestMovesDatabase["k7/8/K7/8/8/7r/7R/8 w - - 10 10"] = "11-obvious gain whites:h2h3"; //obvious gain
	//bestMovesDatabase["K7/8/k7/8/8/7R/7r/8 b - - 10 10"] = "10-obvious gain blacks:h2h3"; //obvious gain

	/*
	bestMovesDatabase["r3q2k/pppppppp/8/1N6/8/8/PPPPPPPP/R3Q2K w - - 10 10"] = "07-minor fork (rook-a8 queen-e8):b5c7";
	bestMovesDatabase["r3q2k/pppppppp/8/8/1n6/8/PPPPPPPP/R3Q2K b - - 10 10"] = "05-minor fork (rook-a1 queen-e1):b4c2";

	bestMovesDatabase["1k6/8/1K6/8/7R/8/8/8 w - - 10 10"] = "01-obvious mate1 (white to play):h4h8"; //obvious mate1
	bestMovesDatabase["r3k3/pppppppp/8/1N6/1n6/8/PPPPPPPP/R3K3 w - - 10 10"] ="06-fork (rook-a8 king-e8):b5c7";
	bestMovesDatabase["8/4B3/2K5/8/8/6k1/7q/8 w - - 0 10"] = "15-bishop pin e7d6(-650)-****(-650) then d6h2(+300):e7d6";

	bestMovesDatabase["k7/8/K7/8/8/6Rr/8/8 w - - 10 10"] = "08-dilema gain or mate (white to move):g3g8"; //dilema gain or mate
	bestMovesDatabase["k7/8/K7/8/8/7r/7R/8 w - - 10 10"] = "11-obvious gain whites:h2h3"; //obvious gain
	bestMovesDatabase["k7/8/8/K7/8/8/PPPPPPPP/r3R2q w - - 10 10"] = "12-best gain (rook-a1 or queen-h1):e1h1";


	bestMovesDatabase["K7/8/k7/8/8/6rR/8/8 b - - 10 10"] = "09-dilema gain or mate (black to move):g3g8"; //dilema gain or mate
	bestMovesDatabase["8/1Q6/2K5/8/8/6k1/4b3/8 b - - 0 10"] = "03-bishop pin:e2f3";

	bestMovesDatabase["6k1/4pp2/3p2p1/2p4p/1pPqPPPP/1P1P4/3Q2K1/5r2 b - - 0 36"] = "04-back mates in 2 (Queen's rush):d4g1"; //mate in 2
	bestMovesDatabase["r3k3/pppppppp/8/1N6/1n6/8/PPPPPPPP/R3K3 b - - 10 10 "] ="16-fork (rook-a1 king-e1):b4c2";
	bestMovesDatabase["r1b1kbnr/ppq1pppp/2n5/8/3PPB2/5N2/PPP2PPP/R2QKB1R b KQkq - 1 7"] = "13-Queen free to take bishop:c7f4";
	bestMovesDatabase["1K6/8/1k6/8/7r/8/8/8 b - - 10 10"] ="14-obvious mate1 (black to play):h4h8"; //obvious mate1
	*/
}

void DebugTest::initLegalMovesDatabase(map<string,string> & testPositions)
{

	testPositions["r3k2r/8/8/8/8/p6p/P6P/R3K3 w KQkq - 1 1"] ="casteling moves:a1b1 a1c1 a1d1 e1d1 e1d2 e1e2 e1f2 e1f1 e1c1";
	testPositions["rnb1k2r/3pPp2/2B3pp/pp6/7B/b1P1PP1P/1P4P1/R3K1NR b - - 0 15"] ="no Black king legal moves detection:a8a7 a8a6 b8a6 b8c6 c8b7 c8a6 h8g8 h8f8 h8h7 d7c6 a3b2 b5b4 g6g5 f7f5 h6h5 f7f6 a5a4 a3e7 a3d6 a3c5 a3b4";
	testPositions["rnbqkbnr/4pQ2/2pp3p/6B1/p1BPP3/2N2NP1/PPP2PP1/R3K2R b KQkq - 0 11"] ="check - only one move 2:e8d7";
	testPositions["rnb1k2r/3p1p2/3b2pp/pp6/4n3/PPP1PPPP/PPP1K1PP/RB1B1BNR w - - 2 14"] ="avoid unlegal exposed square d2:e2d3 e2e1 a3a4 b3b4 c3c4 f3e4 f3f4 g3g4 h3h4";
	testPositions["k6K/8/8/8/8/8/PPPPPPPP/8 w - - 1 1"] ="pawn moves 1:a2a3 a2a4 b2b3 b2b4 c2c3 c2c4 d2d3 d2d4 e2e3 e2e4 f2f3 f2f4 g2g3 g2g4 h2h3 h2h4 h8g8 h8g7 h8h7"; //w pawn moves
	testPositions["k1K5/p7/8/8/8/8/8/8 b - - 1 1"] ="pawn + king:a7a6 a7a5"; //b basic pawn moves
	testPositions["rn2kbnr/2Nbpppp/8/2pP4/3P1B2/P7/P1P2PPP/R2QKB1R b KQkq - 1 1"] ="check - only one move 1:e8d8";
	testPositions["K6k/8/8/8/8/8/8/8 b - - 0 1"] ="king moves:h8g8 h8g7 h8h7";
	testPositions["rB2kbbr/1p6/8/p6Q/2PPP3/8/PP3PPP/R4RK1 b - - 0 19"] ="blacks in check - 5 moves:h8h5 g8f7 e8e7 e8d7 e8d8";
	testPositions["4kb1r/4b3/8/7B/8/8/8/6K1 b k - 0 19"] = "Blacks in check - escape or take(3 moves):h8h5 e8d8 e8d7";
	testPositions["K1k5/8/8/8/3R4/8/8/8 w - - 0 1 "] = "tower moves 1:d4d1 d4d2 d4d3 d4d5 d4d6 d4d7 d4d8 d4a4 d4b4 d4c4 d4e4 d4f4 d4g4 d4h4 a8a7";
	testPositions["K1k5/8/3p4/8/1P1R2p1/3P4/8/8 w - - 0 1 "] ="tower moves 2:d4d5 d4d6 d4c4 d4e4 d4f4 d4g4 a8a7 b4b5";
	testPositions["K1k5/8/8/8/8/3B4/8/8 w - - 0 1 "] = "bishop moves 1:d3b1 d3c2 d3e4 d3f5 d3g6 d3h7 d3a6 d3b5 d3c4 d3e2 d3f1 a8a7";
	testPositions["8/8/8/4N3/8/8/8/k6K w - - 10 10"] =" knight moves 1:e5f7 e5g6 e5g4 e5f3 e5d3 e5c4 e5c6 e5d7 h1h2 h1g2 h1g1"; // knight moves
	testPositions["k7/8/8/pP6/8/8/8/K7 w - a6 10 10"] =" en-passant + pawn push 1 (5 moves):a1a2 a1b2 a1b1 b5b6 b5a6"; // kings + en passant
	testPositions["k7/8/8/1Pp5/8/8/8/K7 w - c6 10 10"] =" en-passant + pawn push 2 (5 moves):a1a2 a1b2 a1b1 b5b6 b5c6"; // kings + en passant
	testPositions["rnbqkbnr/ppp4p/3pp1p1/8/2P1p3/5Q2/PP1P1PPP/RNB1KBNR w KQkq - 0 6 "] =" White queen in danger:b1a3 b1c3 e1d1 e1e2 f1e2 f1d3 g1e2 g1h3 a2a3 a2a4 b2b3 b2b4 c4c5 d2d3 d2d4 g2g3 g2g4 h2h3 h2h4 f3d1 f3e2 f3a3 f3b3 f3c3 f3d3 f3e3 f3g3 f3h3 f3e4 f3f4 f3f5 f3f6 f3f7 f3f8 f3g4 f3h5";

	testPositions["rnb1k2r/3p1p2/3b2pp/pp3n2/4nPb1/4P1P1/4P1P1/5K2 w - - 2 14"] =" White king alone:f1e1 f1g1";     
	testPositions["rnb1k2r/3p1p2/2B3pp/pp6/7B/b1P1PP1P/1P4P1/R3K1NR b - - 0 15"] =" Black king unlegal moves detection(only 1):e8f8 a8a7 a8a6 b8a6 b8c6 c8b7 c8a6 h8g8 h8f8 h8h7 d7c6 a3b2 b5b4 g6g5 f7f5 h6h5 f7f6 a5a4 a3e7 a3d6 a3c5 a3f8 a3b4";

	testPositions["7k/1K1n4/8/1n6/8/8/8/8 w - - 0 15"] = "King vs knights(diags only):b7a8 b7c8 b7a6 b7c6";

	//testPositions["rnb1k2r/3p1p2/5Bpp/pp6/8/b1P1PP1P/1P4P1/R1B1K1NR b - - 0 14"] =" King pb with e8d8:e8f8";

	testPositions["k7/8/K7/8/8/7r/6R1/8 b - - 11 1"] = "Mate position but blacks can avoid";
}

void DebugTest::initNoCheck(map<string,string> & noCheck){
	//testPositions["k6R/8/K7/8/8/8/8/8 b - - 10 10"] =" checkmate";
	//testPositions["k7/8/K7/8/8/8/8/7B b - - 10 10"] =" check";
	//testPositions["k7/8/K7/8/8/8/8/6B1 b - - 10 10"] =" nothing";
	noCheck["k7/8/K7/8/8/8/8/6B1 b - - 10 10"] = "1";
	noCheck["k7/8/K7/8/8/6Rr/8/8 w - - 10 10"] = "2"; //dilema gain or mate
	noCheck["K7/8/k7/8/8/6rR/8/8 b - - 10 10"] =  "3"; //dilema gain or mate
	noCheck["1k6/8/1K6/8/7R/8/8/8 w - - 10 10"] =  "4"; //obvious mate1
	noCheck["1K6/8/1k6/8/7r/8/8/8 b - - 10 10"] =  "5";
}
void DebugTest::initInCheck(map<string,string> & inCheck){
	//testPositions["k6R/8/K7/8/8/8/8/8 b - - 10 10"] =" checkmate";
	//testPositions["k7/8/K7/8/8/8/8/7B b - - 10 10"] =" check";
	//testPositions["k7/8/K7/8/8/8/8/6B1 b - - 10 10"] =" nothing";
	inCheck["k7/8/K7/8/8/8/8/7B b - - 10 10"] = "1";
}
void DebugTest::initInCheckmate(map<string,string> & inCheckmate){
	//testPositions["k6R/8/K7/8/8/8/8/8 b - - 10 10"] =" checkmate";
	//testPositions["k7/8/K7/8/8/8/8/7B b - - 10 10"] =" check";
	//testPositions["k7/8/K7/8/8/8/8/6B1 b - - 10 10"] =" nothing";
	inCheckmate["k6R/8/K7/8/8/8/8/8 b - - 10 10"] = "1";
}

bool DebugTest::TestSelectiveDepth(map<string,string> & testPositions, int thinkTime)
{
	bool result = true;

	//Engine engine1;// = new Engine();
	//engine1.init();
	ChessEngine.SetVerboseMode(true);

	//Engine engine2;// = new Engine();
	//engine2.init();
	//engine2.SetVerboseMode(true);

	map<string, string> summary;// = new Dictionary<string, string>();

	//foreach (DictionaryEntry item in testPositions)
	for(map<string,string>::iterator item = testPositions.begin(); item != testPositions.end(); item++)
	{
		string fen = (*item).first;
		const int SLEEPTIME = 15000;

		cout << "*** Calling std version ***" << endl;
		ChessEngine.SetPosition(fen);// pos.SetFromFEN(fen);
		//ChessEngine.debugQuiesce = false;
		ChessEngine.SetDepthMode(6);
		ChessEngine.Start();
		Sleep(SLEEPTIME);
		ChessEngine.Stop();
		int score1 = ChessEngine.GetBestScore();
		Move bm1 = ChessEngine.GetBestMove();

		cout << "*** Calling quiesce version ***" << endl;
		ChessEngine.SetPosition(fen);// pos.SetFromFEN(fen);
		//ChessEngine.debugQuiesce = true;
		ChessEngine.SetDepthMode(6);
		ChessEngine.Start();
		Sleep(SLEEPTIME);
		ChessEngine.Stop();
		int score2 = ChessEngine.GetBestScore();
		Move bm2 = ChessEngine.GetBestMove();

		cout << "##############################" << endl;
		cout << (*item).second << endl;
		cout << "FEN: "<< fen << endl;
		cout << "MinMax AlphaBeta:  best move= " << move_to_string(bm1) << "  score=" << score1 << endl;
		cout << "Negmax AlphaBeta:  best move= " << move_to_string(bm2) << "  score=" << score2 << endl;

		if ((move_to_string(bm1) == move_to_string(bm2))
			&& (score1 == score2))
		{
			cout << "=> OK" << endl;
			summary[(*item).first] = STR_RESULT_OK;
		}
		else
		{
			cout << "=> KO" << endl;
			summary[(*item).first] = STR_RESULT_KO;
		}
		cout << "##############################" << endl;
		cout << endl ;
	}

	DisplayResults("TestSelectiveDepth ", summary);

	return result;
}

void DebugTest::TestAlphaBeta()
{
	//Engine e;// = new Engine();
	ChessEngine.SetPosition("rnbqkbnr/pppp1ppp/8/4p3/8/5N2/PPPPPPPP/RNBQKB1R b KQkq - 0 2");
	ChessEngine.SetMovetimeMode(5000);
	ChessEngine.Start();
}

void DebugTest::TestBitBoardLocalEval()
{
	/*
	Dictionary<string, BitBoardNode> hash1 = new Dictionary<string, BitBoardNode>();
	Dictionary<string, ArrayBoardNode> hash2 = new Dictionary<string, ArrayBoardNode>();

	for (int testNb = 0; testNb < 5; testNb++)
	{
	BitBoard bb = new BitBoard();
	ArrayBoard ab = new ArrayBoard();
	//bb.SetStartPosition();
	//ab.SetStartPosition();
	string fen = getRandomFEN();
	ab.SetFromFEN(fen);
	bb.SetFromFEN(fen);
	BitBoardNode bitNode = new BitBoardNode(Move.Null, bb, 0, hash1);
	ArrayBoardNode arrayNode = new ArrayBoardNode(Move.Null, ab, 0);

	cout << "FEN: " + fen);
	arrayNode.UpdateScore(1, ab.GetTurn());
	cout << "ArrayBoard score=" + arrayNode.GetScore());
	cout << "BitBoard score=" + bitNode.GetScore());
	if (arrayNode.GetScore() == bitNode.GetScore())
	{
	cout << " => ok");
	} else {
	cout << " => KO");
	}

	}* */
}

void DebugTest::TestPositionLoading()
{
	//TODO : this position is incorrectly initialysed
	// position startpos moves d2d4 f7f6 e2e4 f6f5 e4f5 g7g5 c1g5 e8f7 g5e7 f7e7 f2f4 e7e8 b1c3 e8e7 g1f3 e7e8 f1d3 e8e7 f5f6 e7f6 d3h7 f6e6 h7g8 h8g8 g2g4 g8g7 h2h4 e6e7 f4f5 g7g8 f5f6 e7e6 f6f7 g8g7 g4g5 e6e7 h4h5 g7f7 h1f1 f7f3 d1f3 e7d6 f3f8 d8f8 f1f8 d6c6 f8c8 c6b6 c8b8 a8b8 g5g6 c7c6 h5h6 b8f8 g6g7 f8f7 g7g8q b6c7 g8f7 c7d6 f7d7 d6d7 h6h7 d7d8 h7h8q d8d7 e1d2 d7c7 a1f1 c7b6 f1f7 a7a6 f7b7 b6a5 b7e7 c6c5 d4c5 a5b4 c5c6 b4a5 c6c7 a5b4 c7c8q b4a5 c8a6 a5a6 a2a4


}

void DebugTest::TestBitBoards()
{
	/*
	BitBoard bitBoard = new BitBoard();
	ArrayBoard position = new ArrayBoard();

	position.SetStartPosition();
	bitBoard.SetStartPosition();
	//position.SetPiece(WR, Square.A1);
	//bitBoard.SetPiece(WR, Square.A1);

	cout << "sq/ppiece/bbpiece/result");
	for(int i=0;i<64;i++)
	{
	System.Console.Write(Square.ToString(i)+": ");
	Piece pPos = position.GetPiece(i);
	Piece pBb = bitBoard.GetPiece(i);

	System.Console.Write(pPos.ToString() + "/" + pBb.ToString());

	if(pPos == pBb)
	{
	System.Console.Write(" => ok");
	} else
	{
	System.Console.Write(" => KO !!!");
	}
	cout << endl ;
	}
	* */

}

void DebugTest::TestExpectedBestMove(map<string,string> & testPositions, int thinkTime)
{

	Position pos;// = new Position();

	//Engine engine;// = new Engine();
	//engine.init();

	// test if negmax is more accurate
	//engine.debugNegMaxAB = true;
	ChessEngine.SetVerboseMode(true);
	map<string, string> summary;// = new Dictionary<string, string>();
	for(map<string,string>::iterator item = testPositions.begin(); item != testPositions.end(); item++)
		//foreach (DictionaryEntry item in testPositions)
	{
		string fen = (*item).first;
		ChessEngine.SetPosition(fen);// pos.SetFromFEN(fen);
		//toberemoved
		//vector<Move> list;// = new vector<Move>();

		string str = (*item).second;
		size_t pos = str.find(':');
		string expectedMove = str.substr(pos+1);

		ChessEngine.SetTimeLevel(thinkTime);
		//engine.SetDepthMode(depth);
		ChessEngine.Start();
		// Waiting
		Sleep(thinkTime + 1000);
		/////////////////
		//engine.Stop(); // should be facultative

		Move bestMove = ChessEngine.GetBestMove();
		cout << "##############################" << endl;
		cout << (*item).second << endl;
		cout << "FEN: " << fen <<endl;
		cout << "Best move:     " << move_to_string(bestMove) << endl;
		cout << "Expected move: " << expectedMove << endl;

		if (move_to_string(bestMove) == expectedMove)
		{
			cout << "=> OK" << endl;
			//summary[(*item).first] = STR_RESULT_OK;
			summary[(*item).second] = STR_RESULT_OK;
		}
		else
		{
			cout << "=> KO" << endl;
			//summary[(*item).first] = STR_RESULT_KO;
			summary[(*item).second] = STR_RESULT_KO;
		}
		cout << "##############################" << endl;
		cout << endl ;
		//System.Threading.Thread.Sleep(3000); // workaround
	}

	DisplayResults("TestExpectedBestMove ", summary);

}

void DebugTest::DisplayResults(string title, map<string, string> summary)
{

	cout << "############################################################"<< endl;
	cout << title << endl;
	cout << "############################################################"<< endl;
	//foreach (KeyValuePair<string, string> result in summary)
	for(map<string,string>::iterator result = summary.begin(); result != summary.end(); result++)
	{
		int keySize = (*result).first.size();
		cout << (*result).second << " " << (*result).first.substr(0, min(53, keySize)) << endl;
	}
	cout << "############################################################"<< endl;

}

void DebugTest::TestIsCheck(map<string,string> & noCheck, map<string,string> & inCheck, map<string,string> & inCheckmate)
{

	Position pos;// = new Position();
	map<string, string> noCheckSummary;
	map<string, string> inCheckSummary;
	map<string, string> inCheckmateSummary;
	//map<string,string> testPositions;// = new Hashtable();
	//testPositions["k6R/8/K7/8/8/8/8/8 b - - 10 10"] =" checkmate";
	//testPositions["k7/8/K7/8/8/8/8/7B b - - 10 10"] =" check";
	//testPositions["k7/8/K7/8/8/8/8/6B1 b - - 10 10"] =" nothing";
	////vector<Move> expectedMoves = new vector<Move>();



	for(map<string,string>::iterator item = noCheck.begin() ; item != noCheck.end(); item++)
		//foreach (DictionaryEntry item in testPositions)
	{
		string fen = (*item).first;
		cout << "----------------------------" << endl;
		cout << "FEN : " << fen << endl;
		pos.SetFromFEN(fen);
		bool isCheck = ChessEngine.IsCheck(pos);
		bool isCheckmate = ChessEngine.IsCheckmate(pos);

		if (!isCheck && !isCheckmate)
		{
			cout << STR_RESULT_OK << endl;
			noCheckSummary[(*item).second] = STR_RESULT_OK;
		}
		else
		{
			cout << STR_RESULT_KO << endl;
			noCheckSummary[(*item).second] = STR_RESULT_KO;
		}

	}
	cout << endl ;

	for(map<string,string>::iterator item = inCheck.begin() ; item != inCheck.end(); item++)
		//foreach (DictionaryEntry item in testPositions)
	{
		string fen = (*item).first;
		cout << "----------------------------" << endl;
		cout << "FEN : " << fen << endl;
		pos.SetFromFEN(fen);
		bool isCheck = ChessEngine.IsCheck(pos);
		bool isCheckmate = ChessEngine.IsCheckmate(pos);

		if (isCheck)
		{
			cout << STR_RESULT_OK ;//<< endl;
			inCheckSummary[(*item).second] = STR_RESULT_OK;
		}
		else
		{
			cout << STR_RESULT_KO ;//<< endl;
			inCheckSummary[(*item).second] = STR_RESULT_KO;
		}

	}
	cout << endl ;


	for(map<string,string>::iterator item = inCheckmate.begin() ; item != inCheckmate.end(); item++)
		//foreach (DictionaryEntry item in testPositions)
	{
		string fen = (*item).first;
		cout << "----------------------------" << endl;
		cout << "FEN : " << fen << endl;
		pos.SetFromFEN(fen);
		bool isCheck = ChessEngine.IsCheck(pos);
		bool isCheckmate = ChessEngine.IsCheckmate(pos);

		if (isCheckmate)
		{
			cout << STR_RESULT_OK ;//<< endl;
			inCheckmateSummary[(*item).second] = STR_RESULT_OK;
		}
		else
		{
			cout << STR_RESULT_KO ;//<< endl;
			inCheckmateSummary[(*item).second] = STR_RESULT_KO;
		}

	}
	cout << endl ;

	DisplayResults("No check positions", noCheckSummary);
	DisplayResults("In check positions", inCheckSummary);
	DisplayResults("In checkmate positions", inCheckmateSummary);
}

bool DebugTest::TestPlayUnplayMoves(map<string,string> & testPositions, bool verbose)
{
	bool result = true;

	map<string, string> summary;

	for(map<string,string>::iterator item = testPositions.begin(); item != testPositions.end(); item++)
		//foreach (DictionaryEntry item in testPositions)
	{
		string fen = (*item).first;
		int pbCount = 0;
		long mvNb = 0;
		if (verbose) 
			cout << "FEN: " << fen << endl;
		Position pos1;// = new Position();
		Position pos2;// = new Position();
		pos1.SetFromFEN(fen);
		pos2.SetFromFEN(fen);
		vector<Move> moves;// = new vector<Move>();
		Engine e;
		e.GetLegalMoves(pos1, moves);
		mvNb = moves.size();
		//foreach (Move m in moves)
		//{
		//for (int i = 0; i < moves.size(); i++)
		for(vector<Move>::iterator m = moves.begin(); m != moves.end(); m++)
			//for(int i=0;i<moves.size();i++)
		{
			//Move m = moves.get(i);

			pos2.Play(*m);
			pos2.Unplay(*m);
			if (!pos2.IsEqual(pos1))
			{
				result = false;
				pbCount++;
				if (verbose) {
					cout << "->pb with move " << move_to_string(*m) << endl;
					cout << "static position (pos1)" << endl;
					pos1.DebugDisplay();
					cout << "move/unmove position (pos2)" << endl;
					pos2.DebugDisplay();
				}
				pos2.SetFromFEN(fen);
			}
		}

		if (pbCount == 0) {
			if (verbose)
				cout << "==> OK" << endl;
			summary[(*item).second] = STR_RESULT_OK;
		}
		else {
			if (verbose)
				cout << "==> pb on " << pbCount << "/" << mvNb << " moves" << endl;
			summary[(*item).second] = STR_RESULT_KO;
		}
	}

	DisplayResults("TestPlayUnplayMoves ", summary);

	return result;
}

bool DebugTest::TestAlphaBetaRecVSIter(map<string,string> & testPositions, int duration)
{
	bool result = true;
	int SLEEPTIME = duration + 1000;

	ChessEngine.SetVerboseMode(true);
	map<string, string> summary;

	//foreach (DictionaryEntry item in testPositions)
	for(map<string,string>::iterator item = testPositions.begin(); item != testPositions.end(); item++)
	{
		string fen = (*item).first;

		cout << "*** Calling recusrive version ***" << endl;
		ChessEngine.SetPosition(fen);// pos.SetFromFEN(fen);
		//ChessEngine.debugNegascout = false;
		ChessEngine.searchImplementation = SEARCH_IMPL_ALPHABETA;
		ChessEngine.SetMovetimeMode(duration);
		//engine1.SetDepthMode(6);
		ChessEngine.Start();
		Sleep(SLEEPTIME);
		//engine1.Stop();
		int score1 = ChessEngine.GetBestScore();
		Move bm1 = ChessEngine.GetBestMove();

		cout << "*** Calling iterative version ***" << endl;
		ChessEngine.SetPosition(fen);// pos.SetFromFEN(fen);
		//ChessEngine.debugNegascout = false;
		ChessEngine.searchImplementation = SEARCH_IMPL_ITERATIVE_AB;
		ChessEngine.SetMovetimeMode(duration);
		//engine1.SetDepthMode(6);
		ChessEngine.Start();
		Sleep(SLEEPTIME);
		//engine1.Stop();
		int score2 = ChessEngine.GetBestScore();
		Move bm2 = ChessEngine.GetBestMove();

		cout << "##############################" << endl;
		cout << ((*item).second) << endl;
		cout << "FEN: " << fen << endl;
		cout << "Recursive AlphaBeta: best move= " << move_to_string(bm1) << "  score=" << score1 << endl;
		cout << "Iterative AlphaBeta: best move= " << move_to_string(bm2) << "  score=" << score2 << endl;

		if ((bm1 == bm2)
			&& (score1 == score2))
		{
			cout << "=> OK" << endl;
			summary[(*item).second] = STR_RESULT_OK;
		}
		else
		{
			cout << "=> KO" << endl;
			summary[(*item).second] = STR_RESULT_KO;
		}
		cout << "##############################" << endl;
		cout << endl ;
	}

	DisplayResults("TestAlphaBetaRecVSIter ", summary);

	return result;
}

bool DebugTest::TestABMinmaxVsNegmax(map<string,string> & testPositions, int time)
{
	bool result = true;

	bool fullDebug = true ;

	ChessEngine.SetVerboseMode(true);
	map<string, string> summary;

	//foreach (DictionaryEntry item in testPositions)
	for(map<string,string>::iterator item = testPositions.begin(); item != testPositions.end(); item++)
	{
		string fen = (*item).first;

		cout << "*** Calling minmax version ***" << endl;
		ChessEngine.SetDebug(fullDebug);
		ChessEngine.SetPosition(fen);// pos.SetFromFEN(fen);
		//ChessEngine.debugNegmax = false;
		ChessEngine.searchImplementation = SEARCH_IMPL_ALPHABETA;
		ChessEngine.SetMovetimeMode(time);
		//engine1.SetDepthMode(6);
		ChessEngine.Start();
		Sleep(time+1000);
		//engine1.Stop();
		int score1 = ChessEngine.GetBestScore();
		Move bm1 = ChessEngine.GetBestMove();

		cout << "*** Calling negmax version ***" << endl;
		ChessEngine.SetDebug(fullDebug);
		ChessEngine.SetPosition(fen);// pos.SetFromFEN(fen);
		//ChessEngine.debugNegmax = true;
		ChessEngine.searchImplementation = SEARCH_IMPL_NEGMAX;
		ChessEngine.SetMovetimeMode(time);
		//engine2.SetDepthMode(6);
		ChessEngine.Start();
		Sleep(time+1000);
		//engine2.Stop();
		int score2 = ChessEngine.GetBestScore();
		Move bm2 = ChessEngine.GetBestMove();

		cout << "##############################" << endl;
		cout << ((*item).second);
		cout << "FEN: " << fen << endl ;
		cout << "MinMax AlphaBeta:  best move= " << move_to_string(bm1) << "  score=" << score1 << endl;
		cout << "Negmax AlphaBeta:  best move= " + move_to_string(bm2) << "  score=" << score2 << endl;

		if ((bm1 == bm2)
			&& (score1 == score2))
		{
			cout << "=> OK" << endl;
			summary[(*item).second] = STR_RESULT_OK;
		}
		else
		{
			cout << "=> KO" << endl;
			summary[(*item).second] = STR_RESULT_KO;
		}
		cout << "##############################" << endl;
		cout << endl ;
	}

	DisplayResults("TestABMinmaxVsNegmax ", summary);

	return result;
}

bool DebugTest::TestABMinmaxVSNegascout(map<string,string> & testPositions, int duration)
{
	bool result = true;

	ChessEngine.SetVerboseMode(true);
	ChessEngine.SetDebug(true);
	map<string, string> summary;

	//foreach (DictionaryEntry item in testPositions)
	for(map<string,string>::iterator item = testPositions.begin(); item != testPositions.end(); item++)
	{
		string fen = (*item).first;
		const int SLEEPTIME = duration+1000;

		cout << "*** Calling minmax version ***" << endl;
		ChessEngine.SetPosition(fen);// pos.SetFromFEN(fen);
		//ChessEngine.debugNegascout = false;
		ChessEngine.searchImplementation = SEARCH_IMPL_ALPHABETA;
		ChessEngine.SetMovetimeMode(duration);
		//engine1.SetDepthMode(6);
		ChessEngine.Start();
		Sleep(SLEEPTIME);
		//engine1.Stop();
		int score1 = ChessEngine.GetBestScore();
		Move bm1 = ChessEngine.GetBestMove();

		cout << "*** Calling negascout version ***" << endl;
		ChessEngine.SetPosition(fen);// pos.SetFromFEN(fen);
		//ChessEngine.debugNegascout = true;
		ChessEngine.searchImplementation = SEARCH_IMPL_NEGASCOUT;
		ChessEngine.SetMovetimeMode(duration);
		//ChessEngine.SetDepthMode(6);
		ChessEngine.Start();
		Sleep(SLEEPTIME);
		//ChessEngine.Stop();
		int score2 = ChessEngine.GetBestScore();
		Move bm2 = ChessEngine.GetBestMove();

		cout << "##############################" << endl;
		cout << ((*item).second);
		cout << "FEN: " << fen << endl;
		cout << "MinMax AlphaBeta:  best move= " << move_to_string(bm1) << "  score=" << score1 << endl;
		cout << "Negsct AlphaBeta:  best move= " << move_to_string(bm2) << "  score=" << score2 << endl;

		if ((bm1 == bm2)
			&& abs(score1-score2)<2) // score cannot be exactly the same value
		{
			cout << "=> OK" << endl;
			summary[(*item).second] = STR_RESULT_OK;
		}
		else
		{
			cout << "=> KO" << endl;
			summary[(*item).second] = STR_RESULT_KO;
		}
		cout << "##############################" << endl;
		cout << endl ;
	}

	DisplayResults("TestABMinmaxVSNegascout ", summary);

	return result;
}

void DebugTest::DisplayNonVerboseTestResult(string testFctName, bool result)
{
	if (result) {
		cout << STR_RESULT_OK;
	}
	else {
		cout << STR_RESULT_KO;
	}

	cout << " " << testFctName;
}

void DebugTest::TestLegalMoves(map<string,string> & testPositions)
{
	vector<Move> foundMoves;// = new vector<Move>();
	//Dictionary<String, String> summary = new Dictionary<string, string>();
	map<string,string> summary;

	//foreach (DictionaryEntry item in testPositions)
	for(map<string,string>::iterator item = testPositions.begin(); item != testPositions.end(); item++)
	{
		//string p = ((*item).second).Split(':');
		string str = (*item).second;
		size_t ind = str.find(':');
		string p0 = str.substr(ind);
		string p1 = str.substr(ind+1,str.size()-ind-1);

		Position pos;// = new Position();
		string fen = (*item).first;
		pos.SetFromFEN(fen);
		ChessEngine.GetLegalMoves(pos, foundMoves);


		vector<Move> expectedMoves = StringToMoves(p1, pos);
		ChessEngine.GetLegalMoves(pos, foundMoves);

		cout << "##############################" << endl;
		cout << p0 << endl;
		cout << "FEN: "<< fen << endl;

		cout << "Found moves:    ";
		DisplayMoves(foundMoves, false);
		cout << endl ;

		cout << "Expected moves: ";
		DisplayMoves(expectedMoves, false);
		cout << endl ;

		if (IsEqual(expectedMoves, foundMoves))
		{
			cout << STR_RESULT_OK << endl;
			summary[(*item).second] = STR_RESULT_OK;
		}
		else
		{
			cout << STR_RESULT_KO << endl;
			summary[(*item).second] = STR_RESULT_KO;
		}
		cout << "##############################" << endl;
		//cout << endl ;
	}

	DisplayResults("TestLegalMoves ", summary);

}

bool DebugTest::IsEqual(vector<Move> expected, vector<Move> list)
{
	bool result = true;
	//if (l1.size() != l2.size())
	//    return false;

	//foreach (Move m1 in l1)
	//{
	for (vector<Move>::iterator m1 = expected.begin(); m1 != expected.end(); m1++)
		//for(int i=0;i<expected.size();i++)
	{
		//Move m1 = expected.get(i);

		bool found = false;
		//foreach (Move m2 in l2)
		//{
		for (vector<Move>::iterator m2 = list.begin(); m2 != list.end(); m2++)
			//for(int i=0;i<list.size();i++)
		{
			//Move m2 = list.get(i);

			//if (from_sq(m1) == from_sq(m2)
			//	&& to_sq(m1) == to_sq(m2)
			//	&& promotion_type(m1) == promotion_type(m2))
			if((*m1)==(*m2))
			{
				found = true;
				break;
			}
		}
		if (!found)
		{
			cout << "Move of the ref list not found: " << move_to_string(*m1) << endl;
			result = false;
		}
	}

	// second pass to detect errors
	//foreach (Move m2 in l2)
	//{
	//for (int i2 = 0; i2 < list.size(); i2++)
	for (vector<Move>::iterator m2 = list.begin(); m2 != list.end(); m2++)
		//for(int i=0;i<list.size();i++)
	{
		//Move m2 = list.get(i);

		bool found = false;
		for (vector<Move>::iterator m1 = expected.begin(); m1 != expected.end(); m1++)
			//for (int i1 = 0; i1 < expected.size(); i1++)
			//for(int i=0;i<expected.size();i++)
		{
			//Move m1 = expected.get(i);

			//if (from_sq(*m1) == from_sq(*m2)
			//	&& to_sq(*m1) == to_sq(*m2)
			//	&& promotion_type(*m1) == promotion_type(*m2))
			if((*m1)==(*m2))
			{
				found = true;
				break;
			}
		}
		if (!found)
		{
			cout << "Move in the compared list not expected: " << move_to_string(*m2) << endl;
			result = false;
		}
	}

	return result;
}


vector<Move> DebugTest::StringToMoves(string s, Position& pos)
{
	vector<Move> moves;// = new vector<Move>();
	istringstream is(s);

	char buffer[6];

	while(is >> buffer){

		//cout << "Move read: [" << buffer << "]" << endl;
		Square from = make_square((File)(buffer[0]-'a'),(Rank)(buffer[1]-'1'));
		Square to = make_square((File)(buffer[2]-'a'),(Rank)(buffer[3]-'1'));
		PieceType prom = (PieceType)buffer[4]; // can be an incorrect implementation

		Move m ;
		if(prom == NO_PIECE_TYPE){
			m = make_move(from,to);//new Move(smoves[i],pos);
		} else {
			m = make_promotion(from,to,prom);//new Move(smoves[i],pos);
		}
		moves.push_back(m);
	}

	//cout << "DEBUG StringToMoves" << endl;
	//cout << "in:  " << s << endl;
	//cout << "out: ";
	//for(vector<Move>::iterator m = moves.begin(); m != moves.end(); m++)
	//	cout << move_to_string(*m) << ' ';
	//cout << endl;

	return moves;
}


void DebugTest::TestOptions()
{
	/*
	Engine engine = new Engine();

	engine.SetOption("PawnValue", "101");
	engine.SetOption("KnightValue", "251");
	engine.SetOption("BishopValue", "253");
	engine.SetOption("RookValue", "501");
	engine.SetOption("QueenValue", "901");
	engine.SetOption("QueekValue", "901");
	engine.SetOption("KingValue", "10001");

	cout << endl ;
	*/
}

void DebugTest::TestHashCode()
{
	/*
	Position randomPosition = getRandomPosition();
	Position& pos1 = new Position(randomPosition);
	Position& pos2 = new Position(randomPosition);

	cout << "Testing hash keys on the same random position (different instances):");
	cout << "pos1 simpleHash : " + pos1.GetSimplifiedHashCode());
	cout << "pos2 simpleHash : " + pos2.GetSimplifiedHashCode());
	cout << "pos1 default hash : " + pos1.GetHashCode());
	cout << "pos2 default hash : " + pos2.GetHashCode());

	pos1.SetStartPosition();
	pos2.SetStartPosition();

	cout << "Testing hash keys on the same random position (different instances):");
	cout << "pos1 simpleHash : " + pos1.GetSimplifiedHashCode());
	cout << "pos2 simpleHash : " + pos2.GetSimplifiedHashCode());
	cout << "pos1 default hash : " + pos1.GetHashCode());
	cout << "pos2 default hash : " + pos2.GetHashCode());

	cout << endl ;
	* */
}

int DebugTest::GetNbPieces(Position& position)
{
	int result = 0;
	for (int i = 0; i < 64; i++)
	{
		if (position.GetPiece((Square)i) != NO_PIECE) 
			result++;
	}
	return result;
}

void DebugTest::DisplayMoves(vector<Move> moves, bool fullNotation)
{
	//foreach (Move m in moves)
	//{
	//for (size_t i = 0; i < moves.size(); i++)
	for(vector<Move>::iterator m = moves.begin() ; m != moves.end(); m++)
		//for(int i=0;i<moves.size();i++)
	{
		//Move m = moves.get(i);
		//Move m = moves[i];

		//if(fullNotation)
		//    System.Console.Write(m.ToString() + " ");
		//else
		cout << move_to_string(*m) << " ";
	}
}


void DebugTest::DisplayCheckStatus(Position& pos)
{
	cout << "Position status: " << endl;
	if (ChessEngine.IsCheckmate(pos))
	{
		cout << "checkmate !";
	}
	else
		if (ChessEngine.IsCheck(pos))
		{
			cout << "check !";
		}
		else
		{
			cout << "no special status";
		}
		cout << endl ;
}


/*
static void DisplayCheckStatus(BitBoard pos)
{
System.Console.Write("Position status: ");
if (BitBoardEngine.IsCheckmate(pos))
{
System.Console.Write("checkmate !");
}
else
if (BitBoardEngine.IsCheck(pos))
{
System.Console.Write("check !");
}
else
{
System.Console.Write("no special status");
}
cout << endl ;
}


static Position getRandomPosition()
{
Position randPos = new Position();
Random random = new Random(System.DateTime.Now.Millisecond);
Piece randPiece;
for (int i = 0; i < 1000; i++)
{
int square = i % 64;
int rand = random.Next(0, 15);
if (rand < 12)
{
randPiece = (Piece)rand;
}
else
{
randPiece = EMPTY;
}
randPos.SetPiece(randPiece, square);
}
return randPos;
}
*/
/*
static string getRandomFEN()
{
char[] pieceCode = { 'p', 'P', 'b', 'B', 'n', 'N', 'r', 'R', 'Q', 'q' };
Random rand = new Random(System.DateTime.Now.Millisecond);
StringBuilder result = new StringBuilder();
for (int i = 0; i < 64; i++)
{
if (i == 20) { result.Append('K'); i++; }
else if (i == 40) { result.Append('k'); i++; }
else
{
if (i > 0 && i < 64 && i % 8 == 0) result.Append("/");
int randVal = rand.Next(0, 12);
result.Append(pieceCode[randVal]);
}
}
result.Append(" w KQkq - 0 1");

return result.ToString();
}
*/

/*
static vector<Move> StringToMoves(String str)
{
vector<Move> result = new vector<Move>();
string[] moves = str.Split(' ');
foreach (string move in moves)
{
result.Add(new Move(position,move));
}
return result;
}


static bool IsEqual(vector<Move> list1, vector<Move> list2)
{
bool r1;
bool r2;
if (list1.size() != list2.size()) return false;
r1 = true;
foreach (Move m1 in list1)
{
r2 = false;
foreach (Move m2 in list2)
{
r2 |= m1.IsEqual(m2);
}
//if(r2 == false)
//{
//    System.Console.Write("missing-"+m1.ToString());
//}
r1 &= r2;
}
return r1;
}
* */


